package com.shuwen.gcdj.service.power;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shuwen.gcdj.bean.PagingReq;
import com.shuwen.gcdj.bean.power.request.*;
import com.shuwen.gcdj.bean.power.response.MenuTreeResp;
import com.shuwen.gcdj.bean.power.response.PowerParentResp;
import com.shuwen.gcdj.bean.role.request.GetAllRoleReq;
import com.shuwen.gcdj.common.enums.LogModuleEnum;
import com.shuwen.gcdj.common.enums.LogTypeEnum;
import com.shuwen.gcdj.common.exception.ApiException;
import com.shuwen.gcdj.bean.AddResp;
import com.shuwen.gcdj.bean.power.response.PowerItemResp;
import com.shuwen.gcdj.common.constant.Constants;
import com.shuwen.gcdj.common.sysLog.AddSysLogUtil;
import com.shuwen.gcdj.common.util.BeanHelper;
import com.shuwen.gcdj.common.util.account.AccountUtil;
import com.shuwen.gcdj.common.util.string.StringUtil;
import com.shuwen.gcdj.dto.account.InitUserDto;
import com.shuwen.gcdj.dto.power.CheckAddRespDto;
import com.shuwen.gcdj.dto.power.SearchPowerApiDto;
import com.shuwen.gcdj.dto.role.SearchRolePowerDto;
import com.shuwen.gcdj.entity.Power;
import com.shuwen.gcdj.entity.RolePower;
import com.shuwen.gcdj.mapper.mysql.PowerMapper;
import com.shuwen.gcdj.mapper.mysql.RolePowerMapper;
import com.shuwen.gcdj.service.BaseService;
import com.shuwen.gcdj.service.api.ApiService;
import com.shuwen.gcdj.service.redis.RedisService;
import com.shuwen.gcdj.service.role.RolePowerService;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;


import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class PowerService extends BaseService<PowerMapper, Power> {

    private final PowerMapper powerMapper;

    private final RolePowerService rolePowerService;
    private final RolePowerMapper rolePowerMapper;
    private final PowerApiService powerApiService;
    private final ApiService apiService;
    private final RedisService redisService;
    private final AddSysLogUtil addSysLogUtil;

    /**
     * 添加权限
     *
     * @param req         AddPowerReq
     * @param initUserDto InitUserDto
     * @return AddResp
     */
    public AddResp createPower(AddPowerReq req, InitUserDto initUserDto) {
        // check add
        CheckAddRespDto check = checkAddReturn(req);
        AddResp resp = new AddResp();

        // add action
        Power r = new Power();
        r.setSort(1);

        if (req.getSort() != null) {
            r.setSort(req.getSort());
        }

        if (initUserDto != null && initUserDto.getUserId() != null) {
            r.setUpdateId(initUserDto.getUserId());
        }

        r.setParentId(req.getParentId());
        r.setIsMenu(req.getIsMenu());
        r.setName(req.getName());
        r.setCode(req.getCode());
        r.setLevel(check.getAddLevel());

        // 编辑已有同名权限
        if (check.getAddType().equals(Constants.ADD_TYPE_WITH_UPDATE)) {
            r.setId(check.getCurrentPower().getId());
            r.setIsDelete(Constants.IS_DELETE_NORMAL);
            r.setUpdateTime(null);
            updateById(r);
            addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_UPDATE.getTypeNum(), LogModuleEnum.LOG_MODULE_POWER.getModuleNum(), "编辑已有同名权限" + r.getCode(), req);
            resp.setId(check.getCurrentPower().getId());

            // 清空用户角色缓存 + 权限缓存
            redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
            redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
            return resp;
        }

        // 新增权限
        if (initUserDto != null) {
            Optional.ofNullable(initUserDto.getUserId()).ifPresent(r::setCreateId);
        }
        boolean addRes = save(r);
        if (addRes) {
            resp.setId(r.getId());
            addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_ADD.getTypeNum(), LogModuleEnum.LOG_MODULE_POWER.getModuleNum(), "添加了一个权限" + r.getCode(), req);
            // 清空用户角色缓存 + 权限缓存
            redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
            redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
            return resp;
        }
        log.error("PowerService.createPowerAction.save({}) error", r);
        return null;
    }

    /**
     * 修改权限
     *
     * @param req         UpdatePowerReq
     * @param initUserDto InitUserDto
     */
    public void updatePower(UpdatePowerReq req, InitUserDto initUserDto) {
        if (req == null) {
            throw new ApiException("更新参数不能为空");
        }

        // 根据权限id更新
        if (req.getId() != null && !req.getId().isEmpty()) {
            updateByPowerId(req, initUserDto);
            return;
        }

        if (req.getCode() == null || req.getCode().isEmpty()) {
            throw new ApiException("权限code和权限id不能同时为空");
        }

        // 根据code更新
        updateByPowerCode(req, initUserDto);

        // 清空用户角色缓存 + 权限缓存
        redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
        redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
    }

    /**
     * 获取单个power
     *
     * @param powerCode String
     * @return Power
     */
    public Power getOneByCode(String powerCode, List<Integer> isDelete) {
        if (powerCode == null || powerCode.isEmpty()) {
            return null;
        }

        QueryWrapper<Power> q = new QueryWrapper<>();
        q.eq("`code`", powerCode);
        if (isDelete == null || isDelete.isEmpty()) {
            isDelete = Lists.newArrayList(Constants.IS_DELETE_NORMAL);
        }
        q.in(Constants.FIELD_KEY_IS_DELETE, isDelete);
        return getOne(q);
    }

    /**
     * 批量删除权限
     *
     * @param powerCode   String
     * @param initUserDto InitUserDto
     */
    public void deletePower(String powerCode, InitUserDto initUserDto) {
        String[] split = powerCode.split(",");
        //        日志使用codes
        List<String> powerCodes = new ArrayList<>();
        for (String s : split) {
            // search one by power code
            Power power = getOneByCode(s, Lists.newArrayList(Constants.IS_DELETE_NORMAL));
            if (power == null) {
                return;
            }

            // search current & sub Powers
            String powerId = power.getId();
            List<Power> powers = searchCurrentAndSubPowers(Lists.newArrayList(powerId));
            log.info("deletePower.searchCurrentAndSubPowers() Powers = {}, initUserDto = {}", powers, initUserDto);

            if (CollectionUtils.isEmpty(powers)) {
                List<String> codes = new ArrayList<>();
                for (Power p : powers) {
                    p.setIsDelete(Constants.IS_DELETE_YES);
                    p.setUpdateTime(null);
                    if (initUserDto != null && initUserDto.getUserId() != null) {
                        p.setUpdateId(initUserDto.getUserId());
                    }
                    powerCodes.add(p.getCode());
                    codes.add(p.getCode());
                }

                // delete power multi
                updateBatchById(powers);

                // 清空用户角色缓存 + 权限缓存
                redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
                redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));

                if (!codes.isEmpty()) {
                    // 删除权限与角色绑定关系
                    SearchRolePowerDto search = new SearchRolePowerDto();
                    search.setPowerCodes(codes);
                    rolePowerService.deleteRolePower(search, initUserDto);

                    // 删除权限与Api绑定关系
                    SearchPowerApiDto ps = new SearchPowerApiDto();
                    ps.setPowerCodes(codes);
                    powerApiService.deletePowerApi(ps, initUserDto);
                }
            }
        }
        addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_DELETE.getTypeNum(), LogModuleEnum.LOG_MODULE_POWER.getModuleNum(), "删除了权限" + powerCodes.toString(), powerCode);
    }


    /**
     * 获取当前权限 + 子集权限
     *
     * @param parentId List<String>
     * @return List<Power>
     */
    public List<Power> searchCurrentAndSubPowers(List<String> parentId) {
        List<Power> c = new ArrayList<>();
        if (parentId == null || parentId.isEmpty()) {
            return c;
        }

        QueryWrapper<Power> qw = new QueryWrapper<>();
        qw.in("id", parentId);
        qw.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        c = powerMapper.selectList(qw);
        if (c == null) {
            c = new ArrayList<>();
        }

        searchSubPowers(parentId, c);
        return c;
    }


    /**
     * 递归调用获取子集权限
     *
     * @param parentId List<String>
     */
    public void searchSubPowers(List<String> parentId, List<Power> c) {
        if (parentId == null || parentId.isEmpty()) {
            return;
        }

        QueryWrapper<Power> qw = new QueryWrapper<>();
        qw.in(Constants.FIELD_KEY_PARENT_ID, parentId);
        qw.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        List<Power> powers = powerMapper.selectList(qw);
        if (powers != null) {
            List<String> newParentId = new ArrayList<>();
            for (com.shuwen.gcdj.entity.Power Power : powers) {
                newParentId.add(Power.getId());
            }
            c.addAll(powers);
            searchSubPowers(newParentId, c);
        }
    }

    private CheckAddRespDto checkAddReturn(AddPowerReq req) {
        if (req.getName() == null || req.getName().length() == 0) {
            throw new ApiException("权限名称不能为空");
        }

        if (req.getCode() == null || req.getCode().length() == 0) {
            throw new ApiException("权限码code不能为空");
        }

        CheckAddRespDto dto = new CheckAddRespDto();
        dto.setAddLevel(1);
        if (req.getParentId() == null) {
            req.setParentId("");
        }

        if (!req.getParentId().equals("")) {
            Power parent = getById(req.getParentId());
            Optional.ofNullable(parent).ifPresent(dto::setParentPower);
            if (parent != null && parent.getLevel() != null) {
                dto.setAddLevel(parent.getLevel() + 1);
            }
        }

        QueryWrapper<Power> qc = new QueryWrapper<>();
        qc.eq("`code`", req.getCode());
        Power pc = getOne(qc);
        if (pc != null) {
            if (pc.getIsDelete().equals(Constants.IS_DELETE_NORMAL)) {
                throw new ApiException("权限码已存在，不能重复添加");
            }

            dto.setCurrentPower(pc);
            dto.setAddType(Constants.ADD_TYPE_WITH_UPDATE);
        }

        QueryWrapper<Power> q = new QueryWrapper<>();
        q.eq(Constants.FIELD_KEY_PARENT_ID, req.getParentId());
        q.eq("name", req.getName());
        Power power = getOne(q);
        if (power != null) {
            if (power.getIsDelete().equals(Constants.IS_DELETE_NORMAL)) {
                throw new ApiException("同一层级权限名已存在");
            }

            dto.setCurrentPower(power);
            dto.setAddType(Constants.ADD_TYPE_WITH_UPDATE);
            return dto;
        }

        // 正常添加
        dto.setAddType(Constants.ADD_TYPE_NORMAL_ADD);
        return dto;
    }

    private void updateByPowerCode(UpdatePowerReq req, InitUserDto initUserDto) {
        // 置空power id
        req.setId(null);

        // 根据power code更新权限
        if (req.getCode() == null || req.getCode().isEmpty()) {
            throw new ApiException("权限码code不能为空");
        }

        Power power = getOneByCode(req.getCode(), Lists.newArrayList(Constants.IS_DELETE_NORMAL, Constants.IS_DELETE_YES));
        if (power == null) {
            throw new ApiException("更新权限记录不存在");
        }

        // 验证同一级别不能同名字
        if (req.getName() != null && !req.getName().isEmpty()) {
            QueryWrapper<Power> ps = new QueryWrapper<>();
            ps.eq(Constants.FIELD_KEY_PARENT_ID, power.getId());
            ps.eq("`name`", req.getName());
            ps.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
            ps.last("limit 1");
            Power searchOne = powerMapper.selectOne(ps);
            if (searchOne != null) {
                if (searchOne.getIsDelete() != null && searchOne.getIsDelete().equals(Constants.IS_DELETE_NORMAL)) {
                    throw new ApiException("同级下权限名称已存在");
                }
                searchOne.setIsDelete(Constants.IS_DELETE_NORMAL);
                setUpdaterId(searchOne, initUserDto);
                BeanUtils.copyProperties(req, searchOne);
                searchOne.setUpdateTime(null);
                updateById(searchOne);
            }
        }
        Power p = new Power();
        //        父id修改 ，level也需要修改
        if (!StringUtils.isEmpty(req.getParentId()) && !req.getParentId().equals(p.getParentId())) {
            Power powerParent = powerMapper.selectById(req.getParentId());
            p.setLevel(powerParent.getLevel() + 1);
        }
        if ("".equals(req.getParentId())) {
            req.setParentId(null);
        }
        if ("".equals(req.getCode())) {
            req.setCode(null);
        }
        setUpdaterId(p, initUserDto);
        BeanUtils.copyProperties(req, p);


        p.setUpdateTime(null);
        updateById(p);

        // 清空用户角色缓存 + 权限缓存
        redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
        redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
    }

    private void setUpdaterId(Power p, InitUserDto initUserDto) {
        if (p == null) {
            return;
        }

        if (initUserDto != null && initUserDto.getUserId() != null && !initUserDto.getUserId().isEmpty()) {
            p.setUpdateId(initUserDto.getUserId());
        }
    }

    private void updateByPowerId(UpdatePowerReq req, InitUserDto initUserDto) {
        if (StringUtils.isEmpty(req.getId())) {
            throw new ApiException("更新权限id不能为空");
        }

        // 根据id修改
        Power cur = checkExistReturn(req.getId());

        // 验证code是否一致
        if (!StringUtils.isEmpty(req.getCode())) {
            Power cpy = getOneByCode(req.getCode(), Lists.newArrayList(Constants.IS_DELETE_NORMAL));
            if (cpy != null && !cpy.getId().equals(cur.getId())) {
                throw new ApiException("权限名称已存在，不能重复设置");
            }
        }

        // 验证同级别名字不能相同
        if (!StringUtils.isEmpty(req.getName())) {
            QueryWrapper<Power> qr = new QueryWrapper<>();
            qr.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
            qr.eq(Constants.FIELD_KEY_PARENT_ID, cur.getId());
            qr.eq("`name`", req.getName());
            Power p = getOne(qr);
            if (p != null && !p.getId().equals(req.getId())) {
                throw new ApiException("同级别已存在相同权限名称，不能编辑重复的权限名称");
            }
        }
//        父id修改 ，level也需要修改
        if (!StringUtils.isEmpty(req.getParentId()) && !req.getParentId().equals(cur.getParentId())) {
            Power power = powerMapper.selectById(req.getParentId());
            cur.setLevel(power.getLevel() + 1);
        }
        if ("".equals(req.getParentId())) {
            req.setParentId(null);
        }
        if ("".equals(req.getCode())) {
            req.setCode(null);
        }
        setUpdaterId(cur, initUserDto);
        BeanUtils.copyProperties(req, cur);
        cur.setUpdateTime(null);
        updateById(cur);

        // 修改权限码 - 需修改role_power/power_api
        if (req.getCode() != null && !req.getCode().isEmpty() && !req.getCode().equals(cur.getCode())) {
            rolePowerService.replacePowerCode(cur.getCode(), req.getCode());
            powerApiService.replacePowerCode(cur.getCode(), req.getCode());
        }

        // 清空用户角色缓存 + 权限缓存
        redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
        redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));
    }

    /**
     * 分页获取所有权限列表
     *
     * @return PowerItemResp
     */
    public Page<PowerItemResp> getAllList(GetAllPowerReq req, PagingReq pagingReq) {
        Page<PowerItemResp> page = new Page<>(pagingReq.getPageNum(), pagingReq.getPageSize(), true);
        IPage<PowerItemResp> allList = powerMapper.getAllList(page, req);
        List<PowerItemResp> res = allList.getRecords();
        Page<PowerItemResp> resPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        resPage.setRecords(res);
        return resPage;
    }

    public PowerItemResp getBypowerId(GetAllRoleReq req, String powerId) {
        return powerMapper.getBypowerId(req, powerId);
    }

    public List<PowerParentResp> getAllParentList() {

        return powerMapper.getAllParentList();
    }

    public List<MenuTreeResp> getMenuTreeByRoleIds(List<String> roleIds) {
        List<MenuTreeResp> resps = new ArrayList<>();
        if (CollectionUtil.isEmpty(roleIds)) return resps;
        //获取角色下的菜单配置
        List<RolePower> rolePowers = rolePowerService.list(new LambdaQueryWrapper<RolePower>()
                .in(RolePower::getRoleId, roleIds));
        List<String> menuIds = rolePowers.stream().map(RolePower::getPowerCode).distinct().collect(Collectors.toList());
        if (CollectionUtil.isEmpty(menuIds)) return resps;
        List<Power> powers = this.list(new LambdaQueryWrapper<Power>()
                .in(Power::getId, menuIds));
        if (CollectionUtil.isEmpty(powers)) return resps;
        List<Power> firstMenus = powers.stream().filter(x -> x.getIsMenu().equals(1) && x.getLevel().equals(1) && x.getIsDelete().equals(0)).collect(Collectors.toList());
        List<MenuTreeResp> all = new ArrayList<>();
        List<MenuTreeResp> parents = new ArrayList<>();
        for (Power power : powers) {
            MenuTreeResp tran = new MenuTreeResp();
            BeanHelper.copyProperties(power, tran);
            all.add(tran);
        }
        for (Power p : firstMenus) {
            MenuTreeResp tran = new MenuTreeResp();
            BeanHelper.copyProperties(p, tran);
            parents.add(tran);
        }

        resps = setMenuTreeResp(all.stream().sorted(Comparator.comparing(MenuTreeResp::getWebId)).collect(Collectors.toList()), parents.stream().sorted(Comparator.comparing(MenuTreeResp::getWebId)).collect(Collectors.toList()));
        return resps;
    }

    /**
     * 同步菜单信息
     *
     * @param req
     * @return
     */
    public Boolean initSyncMenuTempalte(List<MenuSettingReq> req) {
        Boolean resp = false;
        if (req.isEmpty()) return resp;
        List<Power> addModels = new ArrayList<>();
        List<Power> modificationModels = new ArrayList<>();
        List<Power> modelList = treeTransitionList(req, "0", 0);
        List<Power> entitys = powerMapper.selectList(new LambdaQueryWrapper<>());
        //实行插入/更新逻辑
        for (Power p : modelList) {
            Power model = entitys.stream().filter(x -> p.getWebId().equals(x.getWebId())).findFirst().orElse(null);
            //验证有匹配项就更新没有就插入
            if (model != null)//更新数据
            {
                if (!p.getWebParentId().equals(model.getWebParentId())) {
                    //对应父级菜单信息
                    Power parentModel = entitys.stream().filter(x -> p.getWebParentId().equals(x.getWebId())).findFirst().orElse(null);
                    if (parentModel != null) model.setParentId(parentModel.getId());
                    else model.setParentId(p.getId());
                    model.setName(p.getName());
                    model.setRoutePath(p.getRoutePath());
                    model.setIcon(p.getIcon());
                    model.setWebParentId(p.getWebParentId());
                } else {
                    model.setName(p.getName());
                    model.setRoutePath(p.getRoutePath());
                    model.setIcon(p.getIcon());
                }
                modificationModels.add(model);
            } else { //插入数据
                addModels.add(p);
            }
        }
        if (!CollectionUtil.isEmpty(addModels)) resp = this.saveBatch(addModels);
        if (!CollectionUtil.isEmpty(modificationModels)) resp = this.updateBatchById(modificationModels);
        return resp;
    }

    /**
     * @param req      页面参数集合
     * @param parentId 父级Id
     * @param level    页面等级
     * @return
     */
    private List<Power> treeTransitionList(List<MenuSettingReq> req, String parentId, Integer level) {
        List<Power> resp = new ArrayList<>();
        if (req.isEmpty()) return resp;
        level++;
        for (MenuSettingReq item : req) {
            //记录父页面
            Power addModel = new Power();
            String menuId = StringUtil.getNonce();
            addModel.setId(menuId);
            addModel.setCode(StringUtil.getNonce());
            addModel.setName(item.getModuleName());
            addModel.setRoutePath(item.getRoutePath());
            addModel.setParentId(parentId);
            addModel.setIcon(item.getIcon());
            addModel.setLevel(level);
            addModel.setSort(1);
            addModel.setIsHidden(0);
            addModel.setShowType("1,2");
            addModel.setDefaultInfoType("all");
            addModel.setIsMenu(1);
            addModel.setWebId(item.getId());
            addModel.setWebParentId(item.getParentId());
            resp.add(addModel);
            //记录按钮项
            if (!CollectionUtil.isEmpty(item.getBtns())) {
                for (MunuBtnSettingReq btn : item.getBtns()) {
                    Power btnModel = new Power();
                    btnModel.setCode(StringUtil.getNonce());
                    btnModel.setName(btn.getName());
                    btnModel.setParentId(menuId);
                    btnModel.setLevel(level + 1);
                    btnModel.setSort(1);
                    btnModel.setIsHidden(0);
                    btnModel.setShowType("1,2");
                    btnModel.setDefaultInfoType("all");
                    btnModel.setIsMenu(0);
                    btnModel.setWebId(btn.getId());
                    btnModel.setWebParentId(item.getId());
                    btnModel.setIsMenu(0);
                    resp.add(btnModel);
                }
            }
            //使用递归获取所有子页面
            List<Power> childModels = treeTransitionList(item.getChild(), menuId, level);
            resp.addAll(childModels);
        }

        return resp;
    }

    private List<MenuTreeResp> setMenuTreeResp(List<MenuTreeResp> all, List<MenuTreeResp> parents) {
        List<MenuTreeResp> resp = new ArrayList<>();
        if (CollectionUtil.isEmpty(all) || CollectionUtil.isEmpty(parents)) return resp;
        resp.addAll(parents);
        for (MenuTreeResp menu : resp) {
            menu.setBtns(all.stream().filter(x -> x.getIsMenu().equals(0) && x.getParentId().equals(menu.getId())).collect(Collectors.toList()));
            List<MenuTreeResp> childs = all.stream().filter(x -> x.getIsMenu().equals(1) && x.getParentId().equals(menu.getId())).sorted(Comparator.comparing(MenuTreeResp::getWebId)).collect(Collectors.toList());
            menu.setChild(setMenuTreeResp(all, childs.stream().sorted(Comparator.comparing(MenuTreeResp::getWebId)).collect(Collectors.toList())).stream().sorted(Comparator.comparing(MenuTreeResp::getWebId)).collect(Collectors.toList()));
        }
        return resp;
    }
}
